package com.zzw.service.Impl;

import com.zzw.bean.SysDictData;
import com.zzw.bean.Wallet;
import com.zzw.bean.WalletExample;
import com.zzw.dao.SysDictDataMapper;
import com.zzw.dao.WalletMapper;
import com.zzw.service.WalletService;
import com.zzw.utils.OssUtil;
import com.zzw.utils.ZXingCodeHutool;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.awt.image.BufferedImage;
import java.io.*;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * @author 赵志伟
 * @version 1.0
 */
@SuppressWarnings({"all"})
@Slf4j
@Service
public class WalletServiceImpl implements WalletService {

    @Autowired
    private WalletMapper walletMapper;
    @Autowired
    private SysDictDataMapper sysDictDataMapper;

    @Value("${media.upload.path}")
    private String mediaUploadPath;


    @Override
    public List<Wallet> listByCondition(Wallet wallet) {
        WalletExample walletExample = new WalletExample();
        WalletExample.Criteria criteria = walletExample.createCriteria();
        if (wallet.getId() != null) {
            criteria.andIdEqualTo(wallet.getId());
        }
        if (wallet.getUserId() != null) {
            criteria.andUserIdEqualTo(wallet.getUserId());
        }
        if (wallet.getType() != null) {
            criteria.andTypeEqualTo(wallet.getType());
        }
        if (wallet.getAmount() != null) {
            criteria.andAmountEqualTo(wallet.getAmount());
        }
        if (wallet.getCategory() != null) {
            criteria.andCategoryEqualTo(wallet.getCategory());
        }
        if (wallet.getPaymentMethod() != null) {
            criteria.andPaymentMethodEqualTo(wallet.getPaymentMethod());
        }
        if (wallet.getItem() != null) {
            criteria.andItemLike("%" + wallet.getItem() + "%");
        }
        if (wallet.getStatus() != null) {
            criteria.andStatusEqualTo(wallet.getStatus());
        }

        // 添加日期范围查询条件
        if (wallet.getStartDate() != null) {
            criteria.andCreateTimeGreaterThanOrEqualTo(wallet.getStartDate());
        }
        if (wallet.getEndDate() != null) {
            criteria.andCreateTimeLessThanOrEqualTo(wallet.getEndDate());
        }

        // 如果未指定日期，但指定了具体时间
        if (wallet.getCreateTime() != null) {
            criteria.andCreateTimeEqualTo(wallet.getCreateTime());
        }

        walletExample.setOrderByClause("create_time desc");
        return walletMapper.selectByExample(walletExample);
    }

    @Override
    public List<Wallet> getFooterData(Wallet wallet) {
        List<Wallet> footerData = walletMapper.getFooterData(wallet);
        return footerData;
    }

    @Override
    public boolean save(Wallet wallet) {
        wallet.setUserId(0l);
        int i = walletMapper.insertSelective(wallet);
        return i > 0;
    }

    @Override
    public boolean delete(Integer id) {
        int i = walletMapper.deleteByPrimaryKey(id);
        return i > 0;
    }

    @Override
    public boolean update(Wallet wallet) {
        int i = walletMapper.updateByPrimaryKeySelective(wallet);
        return i > 0;
    }

    /**
     * 批量保存钱包数据，使用事务确保数据一致性
     *
     * @param walletList 钱包数据列表
     * @return 成功导入的记录数
     */
    @Override
    @Transactional
    public int batchSave(List<Wallet> walletList) {
        int count = 0;

        // 遍历所有导入的数据进行保存
        for (Wallet wallet : walletList) {
            // 设置默认值
            wallet.setUserId(0L);  // 默认用户ID

            // 如果支付方式为空，设置默认值
            if (wallet.getPaymentMethod() == null || wallet.getPaymentMethod().trim().isEmpty()) {
                wallet.setPaymentMethod("1"); // 默认微信支付
            }

            // 如果创建时间为空，设置为当前时间
            if (wallet.getCreateTime() == null) {
                wallet.setCreateTime(new Date());
            }

            // 设置状态为有效
            wallet.setStatus((byte) 1);

            // 插入数据
            try {
                int result = walletMapper.insertSelective(wallet);
                if (result > 0) {
                    count++;
                }
            } catch (Exception e) {
                // 记录错误日志，但继续处理下一条
                e.printStackTrace();
            }
        }

        return count;
    }

    /**
     * 批量删除钱包数据
     *
     * @param ids ID数组
     * @return 成功删除的记录数
     */
    @Override
    @Transactional
    public int batchDelete(String[] ids) {
        int count = 0;

        if (ids != null && ids.length > 0) {
            for (String idStr : ids) {
                try {
                    int id = Integer.parseInt(idStr);
                    int result = walletMapper.deleteByPrimaryKey(id);
                    if (result > 0) {
                        count++;
                    }
                } catch (NumberFormatException e) {
                    // 忽略非数字ID
                    e.printStackTrace();
                }
            }
        }

        return count;
    }

    /**
     * 获取支出分类统计数据
     *
     * @param wallet 查询条件
     * @return 分类统计数据
     */
    @Override
    public List<Map<String, Object>> getCategoryStatistics(Wallet wallet) {
        // 设置查询条件为支出类型
        Wallet queryWallet = new Wallet();
        queryWallet.setType((byte) 1);  // 支出类型

        // 复制日期范围查询条件
        if (wallet != null) {
            queryWallet.setStartDate(wallet.getStartDate());
            queryWallet.setEndDate(wallet.getEndDate());
        }

        // 查询所有支出数据
        List<Wallet> expenses = walletMapper.selectExpenseGroupByCategory(queryWallet);

        // 构造返回结果
        List<Map<String, Object>> result = new ArrayList<>();

        // 分类名称映射
        Map<String, String> categoryNameMap = new HashMap<>();
        List<SysDictData> sysDictDataList = sysDictDataMapper.selectDictLabel("wallet_category");
        for (SysDictData sysDictData : sysDictDataList) {
            categoryNameMap.put(sysDictData.getDictValue(), sysDictData.getDictLabel());

        }

        if (expenses != null && !expenses.isEmpty()) {
            for (Wallet expense : expenses) {
                Map<String, Object> categoryData = new HashMap<>();
                String categoryName = categoryNameMap.getOrDefault(expense.getCategory(), "未知");
                categoryData.put("name", categoryName);
                categoryData.put("value", expense.getAmount());
                result.add(categoryData);
            }
        }

        return result;
    }

    /**
     * 获取月度收支统计数据
     *
     * @param wallet 查询条件
     * @return 月度统计数据
     */
    @Override
    public Map<String, Object> getMonthlyStatistics(Wallet wallet) {
        // 查询月度收入数据
        List<Map<String, Object>> incomeData = walletMapper.selectMonthlyStatistics((byte) 2, wallet);

        // 查询月度支出数据
        List<Map<String, Object>> expenseData = walletMapper.selectMonthlyStatistics((byte) 1, wallet);

        // 构建结果数据
        Map<String, Object> result = new HashMap<>();
        List<String> months = new ArrayList<>();
        List<BigDecimal> income = new ArrayList<>();
        List<BigDecimal> expense = new ArrayList<>();

        // 合并最近6个月的数据
        Calendar calendar = Calendar.getInstance();
        for (int i = 5; i >= 0; i--) {
            // 获取前i个月
            Calendar tempCal = (Calendar) calendar.clone();
            tempCal.add(Calendar.MONTH, -i);
            String monthStr = String.format("%d-%02d", tempCal.get(Calendar.YEAR), tempCal.get(Calendar.MONTH) + 1);
            months.add(monthStr);

            // 查找收入数据
            BigDecimal monthIncome = findMonthAmount(incomeData, monthStr);
            income.add(monthIncome);

            // 查找支出数据
            BigDecimal monthExpense = findMonthAmount(expenseData, monthStr);
            expense.add(monthExpense);
        }

        result.put("months", months);
        result.put("income", income);
        result.put("expense", expense);

        return result;
    }

    /**
     * 在月度数据中查找指定月份的金额
     *
     * @param monthlyData 月度数据列表
     * @param monthStr    月份字符串 (yyyy-MM)
     * @return 金额
     */
    private BigDecimal findMonthAmount(List<Map<String, Object>> monthlyData, String monthStr) {
        if (monthlyData != null) {
            for (Map<String, Object> data : monthlyData) {
                if (monthStr.equals(data.get("month"))) {
                    return (BigDecimal) data.get("amount");
                }
            }
        }
        return BigDecimal.ZERO;
    }

    //线程安全的集合，存储4个线程生成的压缩包结果
    private ConcurrentLinkedQueue<ByteArrayOutputStream> zipResults = new ConcurrentLinkedQueue<>();

    /**
     * 生成二维码压缩包
     *
     * @param excelFile Excel文件
     * @return 是否成功
     */
    @Override
    public void exportQrCode(MultipartFile excelFile) {
        try {
            XSSFWorkbook workbook = new XSSFWorkbook(excelFile.getInputStream());
            XSSFSheet sheet = workbook.getSheetAt(0);
            //从第二行开始读取第一列
            int startRow = 1;
            int endRow = sheet.getLastRowNum();
            LinkedList<String> ids = new LinkedList<>();
            for (int i = startRow; i <= endRow; i++) {
                String cellValue = sheet.getRow(i).getCell(0).getNumericCellValue() + "";
                ids.add(cellValue);
            }
            workbook.close();//关闭流

            // 使用多线程技术，将ids分开，每个线程处理一部分数据
            List<List<String>> parts = new ArrayList<>();
            int threadCount = 4;
            int partSize = ids.size() / threadCount;
            for (int i = 0; i < threadCount; i++) {
                int fromIndex = i * partSize;
                int toIndex = (i + 1) * partSize;
                if (i == threadCount - 1) {
                    toIndex = ids.size();
                }
                parts.add(ids.subList(fromIndex, toIndex));
            }

            //线程安全的集合，存储4个线程
            List<Thread> generateThreads = new CopyOnWriteArrayList<>();
            //前四个子线程用来生成二维码, 并返回内存输出流
            for (List<String> part : parts) {
                Thread generateThread = new Thread(() -> {
                    generateThreads.add(Thread.currentThread());//把当前线程加入线程池
                    ByteArrayOutputStream byteArrayOutputStream = buildImageIntoMemory(part);
                    if (byteArrayOutputStream != null) {
                        zipResults.add(byteArrayOutputStream);
                    }
                });
                //启动前加入到集合
                generateThreads.add(generateThread);
                //启动线程
                generateThread.start();
            }

            //第5个子线程，用来上传二维码压缩包到OSS
            new Thread(() -> {
                try {
                    for (Thread generateThread : generateThreads) {
                        generateThread.join();//线程插队，让前四个子线程执行完毕后再执行第5个子线程
                    }
                    byte[] zipBytes = mergeZipResults(zipResults);
                    uploadZipFileByStream(zipBytes);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();//中断当前线程
                }

            }).start();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public ByteArrayOutputStream buildImageIntoMemory(List<String> ids) {
        /**
         * 调用hutool工具生成二维码，并返回byteArrayInputStream对象
         * https://www.doubao.com/share/doc/3485e94a0d127f77
         *
         * @param qrCodeUrl    二维码内容（URL）
         * @param productName  产品名称（会显示在二维码下方）
         * @return byteArrayInputStream 二维码图像的字节输入流，需在使用后关闭
         */
        try {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream);
            long startTime = System.currentTimeMillis();
            for (String id : ids) {
                try {
                    // 生成带Logo的二维码并保存到指定路径
                    ByteArrayInputStream byteArrayInputStream = ZXingCodeHutool.getLogoQRCode("http://www.baidu.com?id=" + id, "产品" + id);
                    ZipEntry zipEntry = new ZipEntry("product__" + id + ".png");//显式地设置一下压缩包中的图片名称
                    zipOutputStream.putNextEntry(zipEntry);//把这个“空文件”放进压缩包

                    //把二维码流“灌”进这个空文件里
                    byte[] buffer = new byte[1024 * 8];
                    int len;
                    while ((len = byteArrayInputStream.read(buffer)) != -1) {
                        zipOutputStream.write(buffer, 0, len);//一点一点的把二维码数据写入压缩包
                    }
                    buffer = null;
                    zipOutputStream.closeEntry();
                    byteArrayInputStream.close();
                } catch (Exception e) {
                    continue;
                }
            }
            zipOutputStream.finish();//压缩包结束,确保所有数据都写进了压缩包
            zipOutputStream.close();
            long endTime = System.currentTimeMillis();
            log.info("生成二维码压缩包耗时: {} 秒", (endTime - startTime) / 1000);
            return byteArrayOutputStream;
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //循环zipResults里的内存输出流，获取到压缩包的字节数组，合并到zipBytes
    public byte[] mergeZipResults(ConcurrentLinkedQueue<ByteArrayOutputStream> subZips) {
        ByteArrayOutputStream mergedBaos = new ByteArrayOutputStream();

        try (ZipOutputStream mergedZip = new ZipOutputStream(mergedBaos)) {
            for (ByteArrayOutputStream subBaos : subZips) {
                try (ZipInputStream subZip = new ZipInputStream(new ByteArrayInputStream(subBaos.toByteArray()))) {
                    ZipEntry zipEntry;////创建一个空文件条目，用于保存当前文件条目的数据
                    //读取子ZIP中的每个文件条目，然后合并到主ZIP中
                    while ((zipEntry = subZip.getNextEntry()) != null) {
                        //向合并后的ZIP中添加一个文件条目
                        mergedZip.putNextEntry(new ZipEntry(zipEntry.getName()));//显式地设置一下文件条目的名称
                        //把子ZIP中的文件数据“灌”进合并后的ZIP中
                        byte[] buf = new byte[1024 * 8];
                        int len;
                        while ((len = subZip.read(buf)) != -1) {
                            mergedZip.write(buf, 0, len);
                        }
                        mergedZip.closeEntry();
                        subZip.closeEntry();
                    }
                    subBaos.close();
                } catch (Exception e) {
                    throw new RuntimeException("合并zip失败");
                }finally {
                    subBaos.close();
                }
            }
            mergedZip.finish();
        } catch (Exception e) {
            throw new RuntimeException("合并zip失败");
        }


        return mergedBaos.toByteArray();
    }

    public void uploadZipFileByStream(byte[] zipBytes) {
        try {
            long startTime = System.currentTimeMillis();
            //将字节数组转化为输入流
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(zipBytes);
            //上传到OSS对象存储
            String objectName = "/upload/media/qrCode" + UUID.randomUUID() + ".zip";
            String zipUrl = OssUtil.uploadStream(byteArrayInputStream, objectName, "application/zip");

            long endTime = System.currentTimeMillis();
            log.info("压缩包合并，上传OSS耗时: {} 秒", (endTime - startTime) / 1000);

            //关闭流
            zipBytes = null;
            byteArrayInputStream.close();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
